﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Reflection;

namespace MyTech.DataAccess.Common
{
    public class QueryExecutorMultiDb: IDisposable
    {
        public DbObjectType? DbInstanceType { get; set; }

        private List<IDatabase> SupportedDb { get; set; }

        public QueryExecutorMultiDb() { }

        public void Load()
        {
            SupportedDb = GetDbInstance();
        }

        public int? ExecuteNonQuery(string sqlStmt, DbObjectType dbType)
        {
            int? dataValue = null;
            if (IsAccessAvailable(dbType))
            {
                var dbInstance = SupportedDb.FirstOrDefault(n => n.DbType == dbType);
                if (null != dbInstance)
                {
                    dataValue = dbInstance.ExecuteNonQuery(sqlStmt);
                }
            }
            return dataValue;
        }

        public DataSet ExecuteReader(string sqlStmt,DbObjectType dbType)
        {
            DataSet dataSet = null;
            if (IsAccessAvailable(dbType))
            {
                var dbInstance = SupportedDb.FirstOrDefault(n => n.DbType == dbType);
                if (null != dbInstance)
                {
                    dataSet = dbInstance.ExecuteReader(sqlStmt);
                }
            }
            return dataSet;
        }

        public object ExecuteScalar(string sqlStmt, DbObjectType dbType)
        {
            object  dataValue = null;
            if (IsAccessAvailable(dbType))
            {
                var dbInstance = SupportedDb.FirstOrDefault(n => n.DbType == dbType);
                if (null != dbInstance)
                {
                    dataValue = dbInstance.ExecuteScalar(sqlStmt);
                }
            }

            return dataValue;
        }

        internal List<IDatabase> GetDbInstance()
        {
            var assemblyList = new List<IDatabase>();
            IDatabase dataInstance = null;
        
            if (IsAccessAvailable(DbObjectType.MsAccess))
            {
                if (null != (dataInstance = LoadAssembly(DbObjectType.MsAccess)))
                {
                    assemblyList.Add(dataInstance);
                }
            }
            if (IsAccessAvailable(DbObjectType.MsSQL))
            {
                if (null != (dataInstance = LoadAssembly(DbObjectType.MsSQL)))
                {
                    assemblyList.Add(dataInstance);
                }
            }
            if (IsAccessAvailable(DbObjectType.MySQL))
            {
                if (null != (dataInstance = LoadAssembly(DbObjectType.MySQL)))
                {
                    assemblyList.Add(dataInstance);
                }
            }
            if (IsAccessAvailable(DbObjectType.Oracle))
            {
                if (null != (dataInstance = LoadAssembly(DbObjectType.Oracle)))
                {
                    assemblyList.Add(dataInstance);
                }
            }
            return assemblyList;
        }

        internal bool IsAccessAvailable(DbObjectType dbType)
        {
            bool available = false;

            if (null == DbInstanceType || !DbInstanceType.HasValue)
                throw new ArgumentNullException("DbInstanceType");

            DbObjectType objTYpe = DbInstanceType.Value;

            if ((DbInstanceType.Value & DbObjectType.MsAccess) == dbType)
            {
                available = true;
            }
            else if ((DbInstanceType.Value & DbObjectType.MsSQL) == dbType)
            {
                available = true;
            }
            else if ((DbInstanceType.Value & DbObjectType.MySQL) == dbType)
            {
                available = true;
            }
            else if ((DbInstanceType.Value & DbObjectType.Oracle) == dbType)
            {
                available = true;
            }
            return available;
        }

        internal IDatabase LoadAssembly(DbObjectType dbType)
        {
            IDatabase dataInstance = null;
            var assemblyName = ConfigurationManager.AppSettings[dbType.ToString()];

            if (!string.IsNullOrEmpty(assemblyName))
            {
                var asm = Assembly.LoadFrom(assemblyName);

                var instances = from t in asm.GetTypes()
                                where t.GetInterfaces().Contains(typeof(IDatabase))
                                         && t.GetConstructor(Type.EmptyTypes) != null
                                select Activator.CreateInstance(t) as IDatabase;

                dataInstance = instances.FirstOrDefault();
            }

            return dataInstance;
        }

        public void Dispose()
        {
            //YTB
            //Try to find and db connection or uncommit transactions are availble then close or rollback them.           
        }        
    }
}
